
#ifndef __RIFF_WAVE_H__
#define __RIFF_WAVE_H__ 1

#include <fstream>
#include <string>
#include <stdint.h>

#include "wave_format_tag.h"

/**
 * infos from:
 * http://www.piclist.com/techref/io/serial/midi/wave.html
 *
 *
 *   sample       sample              sample
 *   frame 0      frame 1             frame N
 *  _____ _____ _____ _____         _____ _____
 * | ch1 | ch2 | ch1 | ch2 | . . . | ch1 | ch2 |
 * |_____|_____|_____|_____|       |_____|_____|
 *  _____
 * |     | = one sample point
 * |_____|
 *
 *   channels       1         2
 *              _________ _________
 *             | left    | right   |
 *   stereo    |         |         |
 *             |_________|_________|
 *
 *                  1         2         3
 *              _________ _________ _________
 *             | left    | right   | center  |
 *   3 channel |         |         |         |
 *             |_________|_________|_________|
 *
 *                  1         2         3         4
 *              _________ _________ _________ _________
 *             | front   | front   | rear    | rear    |
 *   quad      | left    | right   | left    | right   |
 *             |_________|_________|_________|_________|
 *
 *                  1         2         3         4
 *              _________ _________ _________ _________
 *             | left    | center  | right   | surround|
 *   4 channel |         |         |         |         |
 *             |_________|_________|_________|_________|
 *
 *                  1         2         3         4         5         6
 *              _________ _________ _________ _________ _________ _________
 *             | left    | left    | center  | right   | right   |surround |
 *   6 channel | center  |         |         | center  |         |         |
 *             |_________|_________|_________|_________|_________|_________|
 *
 */
class CRiffWave
{
public:
	CRiffWave();
	virtual ~CRiffWave();
	
	/**
	 * Open a RIFF WAVE file and validate it.
	 *
	 * @return True if the file is found and validated.
	 */
	bool open(const char* pFilename);
	
	/**
	 * Close wav file.
	 *
	 * @return True for success.
	 */
	bool close();
	
	/**
	 * Get information about the opened wav file.
	 *
	 * @return String which contains the infos.
	 */
	std::string getWaveInfo();

	/**
	 * Return count of samples.
	 * If the wave file has two channels then the value is the count of both
	 * channels.
	 *
	 * @return Count of samples from all channels
	 */
	unsigned int sampleCount();

	unsigned int frameCount();
	
	/**
	 * Get number of channels.
	 * Would be return 1 for mono and 2 for stereo.
	 *
	 * @return Number of channels.
	 */
	unsigned int channels();

	unsigned int frameRate();

	unsigned int bitsPerSample();

	/**
	 * Alignment of one sample.
	 */
	unsigned int bytePerSample();

	/**
	 * bytePerSample and bytePerFrame would be the same value if there is only
	 * one channel. If the wave file is stereo then bytePerFrame is the double
	 * of bytePerSample.
	 */
	unsigned int bytePerFrame();

	NWaveFormatTag::EFormatTag getFormatTag();

	/**
	 * Set the iterator to the first sample.
	 *
	 * @return True for success.
	 */
	bool begin();
	
	/**
	 * Get the next samples from the current iterator position.
	 *
	 * @param pSamples Array to store the samples.
	 * @param pCount Count of samples which should be stored. Array pSamples
	 *        must has a least pCount elements. One element is equal one sample
	 *        and must have the size of bytePerSample().
	 * @param pStartOffset Stores the offset of the first sample. This value
	 *        can be useful for pOffset from writeSamples methode. The offset
	 *        is sample orientated and not byte orientated.
	 * @return Count of read samples.
	 */
	unsigned int getNextSamples(void* pSamples,
			unsigned int pCount,
			unsigned int &pStartOffset);
	
	/**
	 * Read a count of samples from the wav file.
	 * If the wav file has to channels then the samples are CH1, CH2, CH1, CH2,
	 * CH1, ... and so on.
	 *
	 * @param pSamples Array for storing the samples
	 * @param pOffset Offset of the first sample (unit: 1 sample).
	 * @param pCount Count of samples (unit: 1 sample).
	 * @return Count of read samples.
	 */
	unsigned int readSamples(void* pSamples,
			unsigned int pOffset,
			unsigned int pCount);
	
	/**
	 * Write a count of samples into the wav file.
	 * If the wav file has to channels then the samples must be ordered like
	 * CH1, CH2, CH1, CH2, CH1, ... and so on.
	 *
	 * @param pSamples Array for samples which should be written.
	 * @param pOffset Offset of the first sample in the file (unit: 1 sample).
	 * @param pCount Count of samples (unit: 1 sample).
	 * @return Count of written samples.
	 */
	unsigned int writeSamples(void* pSamples,
			unsigned int pOffset,
			unsigned int pCount);
	
	/**
	 * A demo methode for the use of this class.
	 * Change every second sample.
	 *
	 * @return True for successful execution.
	 */
	bool flipEverySecondSample();
private:
	std::fstream mWav;
	
	struct SFormatChunk
	{
		SFormatChunk()
				:formatTag(0), channels(0), samplesPerSec(0),
						avgBytesPerSec(0), blockAlign(0), bitsPerSample(0) {}
		uint16_t formatTag;
		uint16_t channels;
		uint32_t samplesPerSec;
		uint32_t avgBytesPerSec;
		uint16_t blockAlign;
		uint16_t bitsPerSample;
	} mFormat;
	
	struct SDataChunk
	{
		SDataChunk() :length(0), filePos(0), bytePerSample(0),
				samples(0), currentIndex(0) {}
		uint32_t length;
		std::fstream::pos_type filePos;
		unsigned int bytePerSample;
		unsigned int samples;
		unsigned int currentIndex;
	} mData;
	
	bool openWave();
};

#endif
